/*&h0*/

#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <conio.h>
#include <string.h>

#include "types.h"
#include "getkbd.h"
#include "alloc.h"

typedef struct
	{
	int Key;
	int *List;
	} tMacro;


static int AsciiConv[32] =
	{
	/*^@=*/ 0, /*^A=*/ kC_LEFT, /*^B=*/ kEND, /*^C=*/ kPGDOWN,
	/*^D=*/ kRIGHT, /*^E=*/ kUP, /*^F=*/ kC_RIGHT, /*^G=*/ kDEL,
	/*^H=*/ kBSPACE, /*^I=*/ kTAB, /*^J=*/ kC_CR, /*^K=*/ 0, /*^L=*/ 0,
	/*^M=*/ kCR, /*^N=*/ kC_END, /*^O=*/ kS_TAB, /*^P=*/ kC_BSPACE,
	/*^Q=*/ kHOME, /*^R=*/ kPGUP, /*^S=*/ kLEFT, /*^T=*/ 0, /*^U=*/ kC_HOME,
	/*^V=*/ kINS, /*^W=*/ kC_PGUP, /*^X=*/ kDOWN, /*^Y=*/ 0,
	/*^Z=*/ kC_PGDOWN, /*^[=*/ kESC,
	/*^\=*/ 0, /*^]=*/ 0, /*^^=*/ 0, /*^_=*/ 0
	};

static int *PlayP = NULL;
static int *PlayBuf = NULL;
static int PlayLen = 0;
static int *RecP = NULL;
static int *RecBuf = NULL;
static int RecLen = 0;
static int RecKey = -1;
static tMacro *MacroList = NULL;
static int MacroListLen = 0;
static int MacroDefKey = -1;
static int (*Allowed)(int Key) = NULL;
static void (*Msg)(int Nr) = NULL;
static int CurMsg;


static int ReadKey()
	{
	int c;

	if (isatty(0))
		{
		c = _getch();
		if (c == 0 || c == 0xE0)
			return _getch() + kSCANOFFS ;
		else
			return c ;
		}
	else
		{
		c = getchar();
		if (c > 0 && c < ' ' && AsciiConv[c] != 0)
			return(AsciiConv[c]);
		else
			return(c);
		}
	}


static tMacro *FindMacro(int Key)
	{
	tMacro *mp;

	if (MacroList != NULL)
		for (mp = MacroList ; mp->Key >= 0 ; mp++)
			if (mp->Key == Key)
				return (mp);
	return (NULL);
	}


static void MsgOut(int Nr)
	{
	if (Msg != NULL)
		{
		if (Nr != CurMsg)
			(*Msg)(Nr);
		CurMsg = Nr;
		}
	else
		if (Nr == kMACMSG_MEMFULL || Nr == kMACMSG_NOTALLOWED)
			putchar('\007');
	}


static int EndRecord()
	{
	tMacro *mp;
	int *p1, *p2;

	if (MacroList == NULL)
		{
		MacroListLen = 10;
		if ((MacroList = xalloc(tMacro, MacroListLen)) == NULL)
			return (FALSE);
		mp = MacroList;
		mp->Key = -1;
		}
	else
		{
		for (mp = MacroList ; mp->Key >= 0 ; mp++)
			if (mp->Key == RecKey)
				break;
		if (MacroListLen <= mp - MacroList + 1)
			{
			if ((mp = xrealloc(MacroList, tMacro, MacroListLen+10)) == NULL)
				return (FALSE);
			MacroListLen += 10;
			MacroList = mp;
			for (mp = MacroList ; mp->Key >= 0 ; mp++)
				if (mp->Key == RecKey)
					break;
			}
		} /* else MacroList != NULL */
	if ((mp->List = xalloc(int, RecP - RecBuf + 2)) == NULL)
		return (FALSE);
	if (mp->Key < 0)
		(mp+1)->Key = -1;
	mp->Key = RecKey;
	for (p1 = RecBuf, p2 = mp->List ; p1 < RecP ; p1++, p2++)
		*p2 = *p1;
	*p2 = -1;
	return (TRUE);
	}


static int *PlayAlloc(int buflen)
/* Resultat = Startposition zum Kopieren */
	{
	int pos;
	int *p1;

	if (PlayBuf == NULL)
		{
		if ((PlayBuf = xmalloc(int, buflen + 10)) == NULL)
			return (NULL);
		PlayLen = buflen + 10;
		PlayP = NULL;
		p1 = PlayBuf;
		}
	else if (PlayP != NULL)
		for (p1 = PlayBuf ; *p1 >= 0 ; p1++)
			buflen++;
	else
		p1 = PlayBuf;
	if (PlayLen <= buflen)
		{
		if (PlayP != NULL)
			pos = PlayP - PlayBuf;
		if ((p1 = xrealloc(PlayBuf, int, buflen + 10)) == NULL)
			return (NULL);
		PlayLen = buflen + 10;
		PlayBuf = p1;
		if (PlayP != NULL)
			{
			PlayP = PlayBuf + pos;
			for (p1 = PlayBuf ; *p1 >= 0 ; p1++)
				;
			}
		}
	return (p1);
	}


int kbdplaybuf(int *buf)
	{
	int len;
	int *p1, *p2;

	len = 1;
	for (p2 = buf ; *p2 >= 0 ; p2++)
		len++;
	if ((p1 = PlayAlloc(len)) == NULL)
		return (FALSE);
	for (p2 = buf ; *p2 >= 0 ; p2++, p1++)
		*p1 = *p2;
	*p1 = -1;
	if (PlayP == NULL)
		PlayP = PlayBuf;
	return (TRUE);
	}


int kbdplaykey(int key)
	{
	int *p1;

	if ((p1 = PlayAlloc(2)) == NULL)
		return (FALSE);
	p1[0] = key;
	p1[1] = -1;
	if (PlayP == NULL)
		PlayP = PlayBuf;
	return (TRUE);
	}


int kbdplaystr(char *buf)
	{
	int *p1;

	if ((p1 = PlayAlloc(strlen(buf)+1)) == NULL)
		return (FALSE);
	for ( ; *buf != '\0' ; buf++, p1++)
		if (*buf > 0 && *buf < ' ' && AsciiConv[*buf] != 0)
			*p1 = AsciiConv[*buf];
		else
			*p1 = *buf;
	p1[1] = -1;
	if (PlayP == NULL)
		PlayP = PlayBuf;
	return (TRUE);
	}


int getkbd()
	{
	int Key;

	while (TRUE)
		if (PlayP != NULL)
			if (*PlayP < 0)
				PlayP = NULL;
			else
				{
				Key = *PlayP++;
				break;
				}
		else /* PlayP == NULL */
			{
			Key = ReadKey();
			if (Key == MacroDefKey)
				if (RecP == NULL)
					{
					MsgOut(kMACMSG_KEY);
					RecKey = ReadKey();
					if (Allowed != NULL && !(*Allowed)(RecKey))
						MsgOut(kMACMSG_NOTALLOWED);
					else
						{
						RecP = RecBuf;
						MsgOut(kMACMSG_RECORD);
						}
					} /* if RecP == NULL */
				else /* RecP != NULL */
					{
					MsgOut(EndRecord() ? kMACMSG_NO : kMACMSG_MEMFULL);
					RecP = NULL;
					} /* else RecP != NULL */
			else if ((Allowed == NULL || (*Allowed)(Key))
					 && FindMacro(Key) != NULL)
				{
				if (!kbdplaybuf(FindMacro(Key)->List))
					MsgOut(kMACMSG_MEMFULL);
				}
			else
				break;
			} /* else PlayP == NULL */
	if (RecP != NULL)
		{
		if (RecLen <= RecP - RecBuf + 1)
			{
			*RecP = -1;
			if ((RecP = xrealloc(RecBuf, int, RecLen + 20)) == NULL)
				{
				MsgOut(kMACMSG_MEMFULL);
				RecP = NULL;
				return (Key);
				}
			RecLen += 20;
			RecBuf = RecP;
			while (*RecP >= 0)
				RecP++;
			}
		*RecP++ = Key;
		} /* RecP != NULL */
	else
		MsgOut(kMACMSG_NO);
	return (Key);
	}


void skipkbdbuf()
	{
	if (isatty(0))
		while ((PlayP == NULL || *PlayP < 0) && kbhit())
			getkbd();
	}


int kbdhit()
	{
	return((PlayP != NULL && *PlayP >= 0) || !isatty(0) || kbhit());
	}


int kbdmacroinit(int DefineKey, int (*AllowedMacroKey)(int Key),
				 void (*Message)(int nr))
	{
	if (RecBuf == NULL)
		{
		RecLen = 20;
		if ((RecBuf = xalloc(int, RecLen)) == NULL)
			return (FALSE);
		}
	MacroDefKey = DefineKey;
	Allowed = AllowedMacroKey;
	Msg = Message;
	return (TRUE);
	}


